package com.btb.adapter.controller;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import com.btb.adapter.contract.PaymentService;
import com.btb.adapter.utils.CredentialsUtils;

@RestController
@RequestMapping("/account")
public class AccountController {

	@Autowired
	private Web3j web3;
	@Autowired
	private PaymentService paymentService;

	@GetMapping("/contract/balance")
	public String contractBalance(String address) {
		return paymentService.getBalance();
	}

	@GetMapping("/contract/buy/{orderId}")
	public TransactionReceipt buy(@PathVariable("orderId") final String orderId) {
		return paymentService.buy(
				new BigInteger("20"),
				orderId,
				new BigInteger("1"),
				new BigInteger("1"));
	}

	@GetMapping("/accounts")
	public List<String> accounts() {
		try {
			return web3.ethAccounts().send().getAccounts();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return Arrays.asList("");
	}

	@GetMapping("/balance")
	public String balance(String address) {
		BigDecimal banlance = BigDecimal.ZERO;
		try {
			banlance = new BigDecimal(
					web3.ethGetBalance(address, DefaultBlockParameter.valueOf("LATEST")).send().getBalance());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return banlance.divide(new BigDecimal("10").pow(18)).toPlainString();
	}

	@GetMapping("/transfer")
	public TransactionReceipt transfer(String address, BigDecimal count, String privateKey) {
		Credentials credentials = CredentialsUtils.loadCredentials(privateKey);
		TransactionReceipt transferETH = null;
		try {
			transferETH = transferETH(address, count, credentials);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return transferETH;
	}

	@GetMapping("/transfer2")
	public EthSendTransaction transfer2(String toAddress, long amount, String privateKey) {
		EthSendTransaction ethSendTransaction = null;
		try {
			Credentials credentials = CredentialsUtils.loadCredentials(privateKey);
			String fromAddress = credentials.getAddress();
			EthGetTransactionCount ethGetTransactionCount = web3
					.ethGetTransactionCount(fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
			BigInteger nonce = ethGetTransactionCount.getTransactionCount();
			Address address = new Address(toAddress);
			BigDecimal wei = Convert.toWei(new BigDecimal(amount), Convert.Unit.ETHER);
			Uint256 value = new Uint256(BigInteger.valueOf(wei.longValue()));
			List<Type> parametersList = new ArrayList<>();
			parametersList.add(address);
			parametersList.add(value);
			List<TypeReference<?>> outList = new ArrayList<>();
			Function function = new Function("transfer", parametersList, outList);
			String encodedFunction = FunctionEncoder.encode(function);
			RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, Transfer.GAS_LIMIT,
					new BigInteger("1000000"), toAddress, encodedFunction);
			byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
			String hexValue = Numeric.toHexString(signedMessage);
			ethSendTransaction = web3.ethSendRawTransaction(hexValue).sendAsync().get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return ethSendTransaction;
	}

	/**
	 * ETH单笔转账
	 *
	 * @param address     to地址
	 * @param count       to数量
	 * @param credentials from钱包对象
	 */
	public TransactionReceipt transferETH(String address, BigDecimal count, Credentials credentials) throws Exception {
		return Transfer.sendFunds(web3, credentials, address, count, Convert.Unit.ETHER).send();
	}

	/**
	 * 创建钱包
	 *
	 * @param password 生成的keyStore密码
	 * @param outPath  生成的keyStore文件存放路径
	 */
	@GetMapping("/createWallet")
	public String createWallet(String password, String outPath) throws Exception {
		// 创建新钱包KeyStore文件
		File floder = new File(outPath);
		String fileName = WalletUtils.generateFullNewWalletFile(password, floder);
		// 通过KeyStore文件加载钱包
		File filePath = new File(floder, fileName);
		Credentials credentials = WalletUtils.loadCredentials(password, filePath.getAbsolutePath());
		// 通过钱包获取私钥
		BigInteger privateKey = credentials.getEcKeyPair().getPrivateKey();
		return privateKey.toString(16);
	}

}
